package seed

import (
	"context"
	"database/sql"
	"encoding/json"
	"fmt"
	"strings"
	"time"

	"github.com/unkeyed/unkey/go/internal/services/keys"
	"github.com/unkeyed/unkey/go/pkg/cli"
	"github.com/unkeyed/unkey/go/pkg/db"
	dbtype "github.com/unkeyed/unkey/go/pkg/db/types"
	"github.com/unkeyed/unkey/go/pkg/otel/logging"
	"github.com/unkeyed/unkey/go/pkg/uid"
)

var localCmd = &cli.Command{
	Name:  "local",
	Usage: "Seed database with workspace, project, environment, API, and root key for local development",
	Flags: []cli.Flag{
		cli.String("database-primary", "MySQL database DSN", cli.Default("unkey:password@tcp(127.0.0.1:3306)/unkey?parseTime=true&interpolateParams=true"), cli.EnvVar("UNKEY_DATABASE_PRIMARY")),
		cli.String("slug", "Slug used to generate all IDs and names (e.g., 'flo' creates ws_flo, proj_flo, etc.)", cli.Default("local")),
		cli.String("org-id", "Organization ID for auth matching (defaults to org_localdefault for local auth)", cli.Default("org_localdefault")),
	},
	Action: seedLocal,
}

func seedLocal(ctx context.Context, cmd *cli.Command) error {
	logger := logging.New()

	database, err := db.New(db.Config{
		PrimaryDSN:  cmd.RequireString("database-primary"),
		ReadOnlyDSN: "",
		Logger:      logger,
	})
	if err != nil {
		return fmt.Errorf("failed to connect to MySQL: %w", err)
	}

	keyService, err := keys.New(keys.Config{
		Logger:       logger,
		DB:           database,
		RateLimiter:  nil,
		RBAC:         nil,
		Clickhouse:   nil,
		Region:       "local",
		UsageLimiter: nil,
		KeyCache:     nil,
	})
	if err != nil {
		return fmt.Errorf("failed to create key service: %w", err)
	}

	slug := cmd.String("slug")
	orgID := cmd.String("org-id")
	now := time.Now().UnixMilli()

	titleCase := strings.ToUpper(slug[:1]) + slug[1:]
	workspaceID := fmt.Sprintf("ws_%s", slug)
	workspaceName := fmt.Sprintf("Org %s", titleCase)
	projectID := fmt.Sprintf("proj_%s", slug)
	envID := fmt.Sprintf("env_%s", slug)
	rootWorkspaceID := fmt.Sprintf("ws_%s_root", slug)
	rootKeySpaceID := fmt.Sprintf("ks_%s_root_keys", slug)
	rootApiID := fmt.Sprintf("api_%s_root_keys", slug)
	userKeySpaceID := fmt.Sprintf("ks_%s", slug)
	userApiID := fmt.Sprintf("api_%s", slug)

	rootKeyID := uid.New(uid.KeyPrefix)
	keyResult, err := keyService.CreateKey(ctx, keys.CreateKeyRequest{
		Prefix:     "unkey",
		ByteLength: 16,
	})
	if err != nil {
		return fmt.Errorf("failed to generate root key: %w", err)
	}

	err = db.Tx(ctx, database.RW(), func(ctx context.Context, tx db.DBTX) error {
		err = db.BulkQuery.UpsertWorkspace(ctx, tx, []db.UpsertWorkspaceParams{
			{
				ID:           workspaceID,
				OrgID:        orgID,
				Name:         workspaceName,
				Slug:         slug,
				CreatedAtM:   now,
				Tier:         sql.NullString{String: "Free", Valid: true},
				BetaFeatures: json.RawMessage(`{"deployments":true}`),
			},
			{
				ID:           rootWorkspaceID,
				OrgID:        fmt.Sprintf("user_%s", slug),
				Name:         "Unkey",
				Slug:         fmt.Sprintf("unkey-%s", slug),
				CreatedAtM:   now,
				Tier:         sql.NullString{String: "Free", Valid: true},
				BetaFeatures: json.RawMessage(`{}`),
			},
		})
		if err != nil {
			return fmt.Errorf("failed to create workspaces: %w", err)
		}

		err = db.BulkQuery.UpsertQuota(ctx, tx, []db.UpsertQuotaParams{
			{
				WorkspaceID:            workspaceID,
				RequestsPerMonth:       150000,
				AuditLogsRetentionDays: 30,
				LogsRetentionDays:      7,
				Team:                   false,
			},
			{
				WorkspaceID:            rootWorkspaceID,
				RequestsPerMonth:       150000,
				AuditLogsRetentionDays: 30,
				LogsRetentionDays:      7,
				Team:                   false,
			},
		})
		if err != nil {
			return fmt.Errorf("failed to create quotas: %w", err)
		}

		err = db.Query.InsertProject(ctx, tx, db.InsertProjectParams{
			ID:               projectID,
			WorkspaceID:      workspaceID,
			Name:             fmt.Sprintf("%s API", titleCase),
			Slug:             fmt.Sprintf("%s-api", slug),
			GitRepositoryUrl: sql.NullString{},
			DefaultBranch:    sql.NullString{},
			DeleteProtection: sql.NullBool{},
			CreatedAt:        now,
			UpdatedAt:        sql.NullInt64{},
		})
		if err != nil && !db.IsDuplicateKeyError(err) {
			return fmt.Errorf("failed to create project: %w", err)
		}

		err = db.Query.UpsertEnvironment(ctx, tx, db.UpsertEnvironmentParams{
			ID:            envID,
			WorkspaceID:   workspaceID,
			ProjectID:     projectID,
			Slug:          "development",
			GatewayConfig: []byte("{}"),
			CreatedAt:     now,
		})
		if err != nil {
			return fmt.Errorf("failed to create environment: %w", err)
		}

		err = db.BulkQuery.UpsertKeySpace(ctx, tx, []db.UpsertKeySpaceParams{
			{
				ID:                 rootKeySpaceID,
				WorkspaceID:        rootWorkspaceID,
				CreatedAtM:         now,
				DefaultPrefix:      sql.NullString{String: "unkey", Valid: true},
				DefaultBytes:       sql.NullInt32{Int32: 16, Valid: true},
				StoreEncryptedKeys: false,
			},
			{
				ID:                 userKeySpaceID,
				WorkspaceID:        workspaceID,
				CreatedAtM:         now,
				DefaultPrefix:      sql.NullString{String: "sk", Valid: true},
				DefaultBytes:       sql.NullInt32{Int32: 16, Valid: true},
				StoreEncryptedKeys: true,
			},
		})
		if err != nil {
			return fmt.Errorf("failed to create key spaces: %w", err)
		}

		err = db.BulkQuery.InsertApis(ctx, tx, []db.InsertApiParams{
			{
				ID:          rootApiID,
				Name:        "Unkey",
				WorkspaceID: rootWorkspaceID,
				AuthType:    db.NullApisAuthType{Valid: true, ApisAuthType: db.ApisAuthTypeKey},
				IpWhitelist: sql.NullString{},
				KeyAuthID:   sql.NullString{String: rootKeySpaceID, Valid: true},
				CreatedAtM:  now,
			},
			{
				ID:          userApiID,
				Name:        fmt.Sprintf("%s API", titleCase),
				WorkspaceID: workspaceID,
				AuthType:    db.NullApisAuthType{Valid: true, ApisAuthType: db.ApisAuthTypeKey},
				IpWhitelist: sql.NullString{},
				KeyAuthID:   sql.NullString{String: userKeySpaceID, Valid: true},
				CreatedAtM:  now,
			},
		})
		if err != nil && !db.IsDuplicateKeyError(err) {
			return fmt.Errorf("failed to create APIs: %w", err)
		}

		err = db.Query.InsertKey(ctx, tx, db.InsertKeyParams{
			ID:                 rootKeyID,
			KeySpaceID:         rootKeySpaceID,
			Hash:               keyResult.Hash,
			Start:              keyResult.Start,
			WorkspaceID:        rootWorkspaceID,
			ForWorkspaceID:     sql.NullString{String: workspaceID, Valid: true},
			Name:               sql.NullString{String: fmt.Sprintf("%s Dev Root Key", titleCase), Valid: true},
			IdentityID:         sql.NullString{},
			Meta:               sql.NullString{},
			Expires:            sql.NullTime{},
			CreatedAtM:         now,
			Enabled:            true,
			RemainingRequests:  sql.NullInt32{},
			RefillDay:          sql.NullInt16{},
			RefillAmount:       sql.NullInt32{},
			PendingMigrationID: sql.NullString{},
		})
		if err != nil && !db.IsDuplicateKeyError(err) {
			return fmt.Errorf("failed to create root key: %w", err)
		}

		allPermissions := []string{
			"api.*.create_api",
			"api.*.read_api",
			"api.*.delete_api",
			"api.*.create_key",
			"api.*.read_key",
			"api.*.update_key",
			"api.*.delete_key",
			"api.*.verify_key",
			"api.*.decrypt_key",
			"api.*.encrypt_key",
			"api.*.read_analytics",
			"identity.*.create_identity",
			"identity.*.read_identity",
			"identity.*.update_identity",
			"identity.*.delete_identity",
			"rbac.*.create_permission",
			"rbac.*.read_permission",
			"rbac.*.delete_permission",
			"rbac.*.create_role",
			"rbac.*.read_role",
			"rbac.*.delete_role",
			"rbac.*.add_permission_to_key",
			"rbac.*.remove_permission_from_key",
			"rbac.*.add_role_to_key",
			"rbac.*.remove_role_from_key",
			"ratelimit.*.create_namespace",
			"ratelimit.*.limit",
			"ratelimit.*.read_override",
			"ratelimit.*.set_override",
			"workspace.*.read_workspace",
		}

		permissionParams := make([]db.InsertPermissionParams, len(allPermissions))
		permissionIDs := make([]string, len(allPermissions))
		for i, perm := range allPermissions {
			permID := uid.New(uid.PermissionPrefix)
			permissionIDs[i] = permID
			permissionParams[i] = db.InsertPermissionParams{
				PermissionID: permID,
				WorkspaceID:  rootWorkspaceID,
				Name:         perm,
				Slug:         perm,
				Description:  dbtype.NullString{Valid: false, String: ""},
				CreatedAtM:   now,
			}
		}

		err = db.BulkQuery.InsertPermissions(ctx, tx, permissionParams)
		if err != nil && !db.IsDuplicateKeyError(err) {
			return fmt.Errorf("failed to insert permissions: %w", err)
		}

		keyPermissionParams := make([]db.InsertKeyPermissionParams, len(allPermissions))
		for i := range allPermissions {
			keyPermissionParams[i] = db.InsertKeyPermissionParams{
				KeyID:        rootKeyID,
				PermissionID: permissionIDs[i],
				WorkspaceID:  rootWorkspaceID,
				CreatedAt:    now,
				UpdatedAt:    sql.NullInt64{},
			}
		}

		err = db.BulkQuery.InsertKeyPermissions(ctx, tx, keyPermissionParams)
		if err != nil && !db.IsDuplicateKeyError(err) {
			return fmt.Errorf("failed to insert key permissions: %w", err)
		}

		return nil
	})
	if err != nil {
		return err
	}

	logger.Info("seed completed",
		"workspace", workspaceID,
		"project", projectID,
		"environment", envID,
		"api", userApiID,
		"keySpace", userKeySpaceID,
		"rootKey", keyResult.Key,
	)

	return nil
}
